package org.tang.hool.webmvc.servlet;

import com.google.gson.Gson;
import org.tang.hool.annotation.Controller;
import org.tang.hool.annotation.RequestMapping;
import org.tang.hool.context.ApplicationContext;
import org.tang.hool.webmvc.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * mvc入口
 *
 * @author Tang Jiujia
 * @since 2022/5/5
 */
@WebServlet(urlPatterns = "/*", loadOnStartup = 1)
public class DispatchServlet extends HttpServlet {

    private final String LOCATION = "classpath:application.properties";

    private List<HandlerMapping> handlerMappings = new ArrayList<>();

    private Map<HandlerMapping, HandlerAdapter> adapterMap = new HashMap<>();

    private List<ViewResolver> viewResolvers = new ArrayList<>();

    private ApplicationContext context;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 初始化IOC容器
        context = new ApplicationContext(LOCATION);
        initStrategies(context);
    }

    /**
     * 初始化处理策略
     *
     * @author Tang Jiujia
     * @since 2022/5/6
     */
    private void initStrategies(ApplicationContext context) {
        initHandlerMappings(context);
        initHandlerAdapter(context);
        initViewResolver(context);
    }

    /**
     * 绑定请求url和controller中的方法
     *
     * @author Tang Jiujia
     * @since 2022/5/6
     */
    private void initHandlerMappings(ApplicationContext context) {
        String[] beanNames = context.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            Object bean = context.getBean(beanName);
            Class<?> cls = bean.getClass();
            if (!cls.isAnnotationPresent(Controller.class)) continue;
            String baseUrl = "";
            if (cls.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping requestMapping = cls.getAnnotation(RequestMapping.class);
                baseUrl = requestMapping.value();
            }

            Method[] methods = cls.getMethods();
            for (Method method : methods) {
                if (!method.isAnnotationPresent(RequestMapping.class)) continue;
                RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                String url = requestMapping.value().replaceAll("\\*", ".*")
                        .replaceAll("/+", "/");
                String regex = (baseUrl + url);
                Pattern pattern = Pattern.compile(regex);
                this.handlerMappings.add(new HandlerMapping(bean, method, pattern));
            }
        }
    }

    /**
     * 初始化handlerAdapter
     *
     * @author Tang Jiujia
     * @since 2022/5/6
     */
    private void initHandlerAdapter(ApplicationContext context) {
        handlerMappings.forEach(handlerMapping -> this.adapterMap.put(handlerMapping, new HandlerAdapter()));
    }

    /**
     * 初始化viewResolver
     *
     * @author Tang Jiujia
     * @since 2022/5/6
     */
    private void initViewResolver(ApplicationContext context) {
        String templateRoot = context.getConfig().getProperty("templateRoot");
        viewResolvers.add(new ViewResolver(templateRoot));
    }

    /**
     * 处理请求逻辑
     *
     * @author Tang Jiujia
     * @since 2022/5/6
     */
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 根据请求报文获取handlerMapping
        HandlerMapping handler = getHandler(req);
        if (handler == null) {
            processDispatchResult(req, resp, new ModelAndView("404"));
            return;
        }
        HandlerAdapter handlerAdapter = getHandlerAdapter(handler);
        Object result = handlerAdapter.handle(req, resp, handler);
        if (result instanceof ModelAndView) {
            ModelAndView modelAndView = (ModelAndView) result;
            processDispatchResult(req, resp, modelAndView);
        } else {
            Gson gson = new Gson();
            String jsonResult = gson.toJson(result);
            resp.setCharacterEncoding("utf-8");
            resp.getWriter().write(jsonResult);
        }
    }

    /**
     * 根据modelAndView生成view并渲染页面
     *
     * @author Tang Jiujia
     * @since 2022/5/6
     */
    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, ModelAndView modelAndView) {
        if (modelAndView == null) return;
        if (this.viewResolvers.isEmpty()) return;
        viewResolvers.forEach(viewResolver -> {
            View view = viewResolver.resolveViewName(modelAndView.getViewName());
            if (view != null) {
                try {
                    view.render(modelAndView.getModel(), req, resp);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return;
            }
        });

    }

    /**
     * 根据请求报文url查找对应的handlerMapping
     *
     * @author Tang Jiujia
     * @since 2022/5/6
     */
    private HandlerMapping getHandler(HttpServletRequest req) {
        if (handlerMappings.isEmpty()) return null;
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");
        for (HandlerMapping handlerMapping : handlerMappings) {
            Matcher matcher = handlerMapping.getPattern().matcher(url);
            if (!matcher.matches()) continue;
            return handlerMapping;
        }
        return null;
    }

    /**
     * 根据handlerMapping获取handlerAdapter
     *
     * @author Tang Jiujia
     * @since 2022/5/6
     */
    private HandlerAdapter getHandlerAdapter(HandlerMapping handlerMapping) {
        if (adapterMap.isEmpty()) return null;
        HandlerAdapter handlerAdapter = adapterMap.get(handlerMapping);
        if (handlerAdapter.support(handlerMapping)) return handlerAdapter;
        return null;
    }
}
